/**
 * $Revision: 1.2 $
 * $Date: 2010/07/10 02:52:48 $
 *
 * Copyright (C) 2006 Jive Software. All rights reserved.
 *
 * This software is published under the terms of the GNU Lesser Public License (LGPL),
 * a copy of which is included in this distribution.
 */

package org.jivesoftware.spark;

import org.jivesoftware.MainWindow;
import org.jivesoftware.MainWindowListener;
import org.jivesoftware.Spark;
import org.jivesoftware.smack.PacketCollector;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketIDFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.FormField;
import org.jivesoftware.smackx.ServiceDiscoveryManager;
import org.jivesoftware.smackx.debugger.EnhancedDebuggerWindow;
import org.jivesoftware.smackx.packet.DataForm;
import org.jivesoftware.smackx.packet.DelayInformation;
import org.jivesoftware.smackx.packet.DiscoverInfo;
import org.jivesoftware.smackx.packet.DiscoverItems;
import org.jivesoftware.smackx.packet.VCard;
import org.jivesoftware.spark.component.tabbedPane.SparkTabbedPane;
import org.jivesoftware.spark.filetransfer.SparkTransferManager;
import org.jivesoftware.spark.search.SearchManager;
import org.jivesoftware.spark.ui.ChatContainer;
import org.jivesoftware.spark.ui.ChatRoom;
import org.jivesoftware.spark.ui.ChatRoomNotFoundException;
import org.jivesoftware.spark.ui.CommandPanel;
import org.jivesoftware.spark.ui.ContactItem;
import org.jivesoftware.spark.ui.ContactList;
import org.jivesoftware.spark.ui.conferences.ConferenceServices;
import org.jivesoftware.spark.ui.status.StatusBar;
import org.jivesoftware.spark.util.ModelUtil;
import org.jivesoftware.spark.util.TaskEngine;
import org.jivesoftware.spark.util.log.Log;
import org.jivesoftware.sparkimpl.plugin.alerts.BroadcastPlugin;
import org.jivesoftware.sparkimpl.plugin.alerts.MessageToTranscript;
import org.jivesoftware.sparkimpl.plugin.bookmarks.BookmarkPlugin;
import org.jivesoftware.sparkimpl.plugin.gateways.GatewayPlugin;
import org.jivesoftware.sparkimpl.plugin.history.RecentContactsPlugin;
import org.jivesoftware.sparkimpl.plugin.manager.Enterprise;
import org.jivesoftware.sparkimpl.plugin.transcripts.ChatTranscriptPlugin;
import org.jivesoftware.sparkimpl.plugin.transcripts.ChatTranscripts;

import cn.newgrand.netcall.offlinefiletransfer.OfflineFilePlugin;
import cn.newgrand.netcall.offlinefiletransfer.OfflineFileTransferRequest;
import cn.newgrand.netcall.offlinefiletransfer.OfflineTransferManager;
import cn.newgrand.smackx.packet.OfflineProxyPacket;

import java.awt.CardLayout;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.util.Iterator;
import java.util.List;
import java.util.TimerTask;

import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;


/**
 * The inner Container for Spark. The Workspace is the container for all plugins into the Spark
 * install. Plugins would use this for the following:
 * <p/>
 * <ul>
 * <li>Add own tab to the main tabbed pane. ex.
 * <p/>
 * <p/>
 * Workspace workspace = SparkManager.getWorkspace();
 * JButton button = new JButton("HELLO SPARK USERS");
 * workspace.getWorkspacePane().addTab("MyPlugin", button);
 * </p>
 * <p/>
 * <li>Retrieve the ContactList.
 */
public class Workspace extends JPanel implements PacketListener {
	
	public static int appOfflineCount =0;
	
    private SparkTabbedPane workspacePane;
    private StatusBar statusBox;
    private CommandPanel commandPanel;
    private ContactList contactList;
    private ConferenceServices conferences;
    private GatewayPlugin gatewayPlugin;
    private BookmarkPlugin bookmarkPlugin;
    private ChatTranscriptPlugin transcriptPlugin;
    private BroadcastPlugin broadcastPlugin;
    private RecentContactsPlugin recentContacts;

    private static Workspace singleton;
    private static final Object LOCK = new Object();
    private static final Object MES_LOCK = new Object();

    private JPanel cardPanel;
    private CardLayout cardLayout;

    public static final String WORKSPACE_PANE = "WORKSPACE_PANE";

    private OfflineFilePlugin offlineFilePlugin;
    /**
     * Returns the singleton instance of <CODE>Workspace</CODE>,
     * creating it if necessary.
     * <p/>
     *
     * @return the singleton instance of <Code>Workspace</CODE>
     */
    public static Workspace getInstance() {
        // Synchronize on LOCK to ensure that we don't end up creating
        // two singletons.
        synchronized (LOCK) {
            if (null == singleton) {
                Workspace controller = new Workspace();
                singleton = controller;
                return controller;
            }
        }
        return singleton;
    }


    /**
     * Creates the instance of the SupportChatWorkspace.
     */
    private Workspace() {
        final MainWindow mainWindow = SparkManager.getMainWindow();

        // Add MainWindow listener
        mainWindow.addMainWindowListener(new MainWindowListener() {
            public void shutdown() {
                final ChatContainer container = SparkManager.getChatManager().getChatContainer();
                // Close all Chats.
                for (ChatRoom chatRoom : container.getChatRooms()) {
                    // Leave ChatRoom
                    container.leaveChatRoom(chatRoom);
                }

                conferences.shutdown();
                gatewayPlugin.shutdown();
                bookmarkPlugin.shutdown();
                broadcastPlugin.shutdown();
            }

            public void mainWindowActivated() {

            }

            public void mainWindowDeactivated() {

            }
        });

        // Initialize workspace pane, defaulting the tabs to the bottom.
        //TODO
        workspacePane = new SparkTabbedPane(JTabbedPane.BOTTOM);

        // Add Panels.
        cardLayout = new CardLayout();
        cardPanel = new JPanel(cardLayout);
        cardPanel.setOpaque(false);
        cardPanel.add(WORKSPACE_PANE, this);

        statusBox = new StatusBar();
        commandPanel = new CommandPanel();

        // Build default workspace
        this.setLayout(new GridBagLayout());
        add(workspacePane, new GridBagConstraints(0, 6, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
        add(statusBox, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
        add(commandPanel, new GridBagConstraints(0, 2, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));


        this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("F12"), "showDebugger");
        this.getActionMap().put("showDebugger", new AbstractAction("showDebugger") {
            public void actionPerformed(ActionEvent evt) {
                EnhancedDebuggerWindow window = EnhancedDebuggerWindow.getInstance();
                window.setVisible(true);
            }
        });

        // Set background
//        setBackground(new Color(239, 237, 237));
//        setBackground(new Color(252, 229, 188));
        setBackground(new Color(244, 243, 242));
//        setOpaque(false);
    }

    /**
     * Builds the Workspace layout.
     */
    public void buildLayout() {
        new Enterprise();

       // Initialize Contact List
        contactList = new ContactList();
        conferences = new ConferenceServices();

        // Init contact list.
        contactList.initialize();

        // Load VCard information for status box
        statusBox.loadVCard();

        // Initialise TransferManager
        SparkTransferManager.getInstance();
        
      //添加常用联系人
        new RecentContactsPlugin().initialize();
        
//        //主界面添加广告
//        new AdvertisementPlugin().initAdMainWin();
    }

    /**
     * Starts the Loading of all Spark Plugins.
     */
    public void loadPlugins() {
        // Add presence and message listeners
        // we listen for these to force open a 1-1 peer chat window from other operators if
        // one isn't already open
        PacketFilter workspaceMessageFilter = new PacketTypeFilter(Message.class);

        // Add the packetListener to this instance
        SparkManager.getSessionManager().getConnection().addPacketListener(this, workspaceMessageFilter);

        // Make presence available to anonymous requests, if from anonymous user in the system.
        PacketListener workspacePresenceListener = new PacketListener() {
            public void processPacket(Packet packet) {
                Presence presence = (Presence)packet;
                if (presence.getProperty("anonymous") != null) {
                    boolean isAvailable = statusBox.getPresence().getMode() == Presence.Mode.available;
                    Presence reply = new Presence(Presence.Type.available);
                    if (!isAvailable) {
                        reply.setType(Presence.Type.unavailable);
                    }
                    reply.setTo(presence.getFrom());
                    SparkManager.getSessionManager().getConnection().sendPacket(reply);
                }
            }
        };

        SparkManager.getSessionManager().getConnection().addPacketListener(workspacePresenceListener, new PacketTypeFilter(Presence.class));

        // Send Available status
        final Presence presence = SparkManager.getWorkspace().getStatusBar().getPresence();
        SparkManager.getSessionManager().changePresence(presence);

        //get offline file
//        ServiceDiscoveryManager disco = ServiceDiscoveryManager.getInstanceFor(SparkManager.getConnection());
//        DiscoverItems items = SparkManager.getSessionManager().getDiscoveredItems();
//        DiscoverInfo info = null;
//        Iterator iter = items.getItems();
//        while (iter.hasNext()) {
//            DiscoverItems.Item item = (DiscoverItems.Item)iter.next();
//            String entity = item.getEntityID();
//            if (entity != null) {
//                if (entity.startsWith("proxy.")) {
//                	try {
//						info = disco.discoverInfo(entity, "offlineSend");
//					} catch (XMPPException e) {
//						// TODO Auto-generated catch block
//						//e.printStackTrace();
//						Log.error("get offline file discover info error",e);
//					}
//                }
//            }
//        }
//        if(info==null){
//        	Log.error("get offline file discover info is null");
//        }else{
//        	String proxyIP ="";
//        	String proxyPort = "";
//        	String proxyJID = "";
//        	String from = info.getFrom();
//        	String to = info.getTo();
//        	DataForm dataForm = (DataForm)info.getExtension("x", "jabber:x:data");
//        	if(dataForm!=null){
//	        	Iterator<FormField> itFormField = dataForm.getFields();
//	        	while(itFormField.hasNext()){
//	        		FormField field = itFormField.next();
//	        		if(field.getVariable().equals("hostinfo")){
//	        			Iterator<String> itHostinfo = field.getValues();
//	        			proxyJID = itHostinfo.next();
//	        			proxyIP = itHostinfo.next();
//	        			proxyPort = itHostinfo.next();
//	        		}else{
//	        			String fileName, fileSize, fileDesc, initiator, md5,dataString;
//	        			Iterator<String> itValues = field.getValues();
//	        			fileName = itValues.next();
//	        			fileSize = itValues.next();
//	        			fileDesc = itValues.next();
//	        			initiator = itValues.next();
//	        			md5 = itValues.next();
//	        			itValues.next();
//	        			dataString = itValues.next();
//	        			OfflineFileTransferRequest of = new OfflineFileTransferRequest(fileName, fileSize, fileDesc, initiator, md5, from,proxyIP,proxyPort,dataString);
//	        			of.setSession("jsi_"+Math.abs(OfflineFileTransferRequest.randomGenerator.nextLong()));
//	        			OfflineTransferManager.handleTransferRequest(of);
//	        		}
//	        	}
//        	}
//        }
        offlineFilePlugin = new OfflineFilePlugin();
        offlineFilePlugin.initialize();
        
        
        // Until we have better plugin management, will init after presence updates.
        gatewayPlugin = new GatewayPlugin();
        gatewayPlugin.initialize();

        // Load all non-presence related items.
        conferences.loadConferenceBookmarks();
        SearchManager.getInstance();
        transcriptPlugin = new ChatTranscriptPlugin();

        // Load Broadcast Plugin
        broadcastPlugin = new BroadcastPlugin();
        broadcastPlugin.initialize();
        
        //load enterprise plugin
//        AppSysMesPlugin appPlugin = new AppSysMesPlugin();
//        appPlugin.initialize();

        // Load BookmarkPlugin
        bookmarkPlugin = new BookmarkPlugin();
        bookmarkPlugin.initialize();

        // Schedule loading of the plugins after two seconds.
        TaskEngine.getInstance().schedule(new TimerTask() {
            public void run() {
                final PluginManager pluginManager = PluginManager.getInstance();
                pluginManager.loadPlugins();
                pluginManager.initializePlugins();

                // Subscriptions are always manual
                Roster roster = SparkManager.getConnection().getRoster();
                roster.setSubscriptionMode(Roster.SubscriptionMode.manual);
            }
        }, 2000);

        // Check URI Mappings
        SparkManager.getChatManager().handleURIMapping(Spark.ARGUMENTS);
    }


    /**
     * Returns the status box for the User.
     *
     * @return the status box for the user.
     */
    public StatusBar getStatusBar() {
        return statusBox;
    }

    /**
     * This is to handle agent to agent conversations.
     *
     * @param packet the smack packet to process.
     */
    public void processPacket(final Packet packet) {
    	
    	
    	SwingUtilities.invokeLater(new Runnable() {
    		public void run() {
    			handleIncomingPacket(packet);
    		}
    	});
    	
    	//原先的考虑:这里采用invokeLater的话，会导致处理消息与组件加载的处理时，线程一直在等待
    	//但是如果采用以下代码，会导致swing多线程问题，抛出莫名其妙的错误。
    	//最后还是采用invokeLater，启用线程
    	//modified by sidney
//    	TaskEngine.getInstance().schedule(new TimerTask() {
//            public void run() {
//                handleIncomingPacket(packet);
//            }
//        },500);
    }


    private void handleIncomingPacket(Packet packet) {
    //控制消息提醒与组件加载的顺序，在组件加载完成后才对消息进行处理。
//    	try {
//    		synchronized (MES_LOCK) {
//    			while(!PluginManager.getInstance().isPluginsInited()){
//    				MES_LOCK.wait(1000);
//        		}
//			}
//		} catch (InterruptedException e1) {
//			// TODO Auto-generated catch block
//			e1.printStackTrace();
//		}
    	
        // We only handle message packets here.
        if (packet instanceof Message) {
            final Message message = (Message)packet;
            boolean isGroupChat = message.getType() == Message.Type.groupchat;
            //截取应用系统消息，并在下面做依次的判断
            PacketExtension appsys = message.getExtension("x", "jabber:x:appsys");
            
            // Check if Conference invite. If so, do not handle here.
            if (message.getExtension("x", "jabber:x:conference") != null) {
            	return;
            }
            
           

            final String body = message.getBody();
            boolean broadcast = message.getProperty("broadcast") != null;

            // Handle offline message.
            DelayInformation offlineInformation = (DelayInformation)message.getExtension("x", "jabber:x:delay");
            if (offlineInformation != null && (Message.Type.chat == message.getType() ||
                Message.Type.normal == message.getType()) && appsys == null) {
//            	TaskEngine.getInstance().schedule(new TimerTask() {
//        			@Override
//        			public void run() {
//        				// TODO Auto-generated method stub
//        				handleOfflineMessage(message);
//        			}
//        		},800);
            	handleOfflineMessage(message);
            }
            
            // 检查是否是应用系统的业务消息，并且将消息保存，
            //这里主要是考虑到 以插件形式 应用系统插件，由于packetlistener 不能接收到 离线消息
            if (offlineInformation!=null && appsys != null) {
            	//保存应用系统的离线消息
            	appOfflineCount++;
            	ChatTranscripts.appendToTranscript("appsys",MessageToTranscript.messageToTranscript(message));
                return;
            }

            if (body == null ||
//                isGroupChat ||
                broadcast ||
                message.getType() == Message.Type.normal ||
                message.getType() == Message.Type.headline ||
                message.getType() == Message.Type.error) {
                return;
            }
            
            // Create new chat room for Agent Invite.
            final String from = packet.getFrom();
            final String host = SparkManager.getSessionManager().getServerAddress();

            // Don't allow workgroup notifications to come through here.
            final String bareJID = StringUtils.parseBareAddress(from);
            if (host.equalsIgnoreCase(from) || from == null) {
                return;
            }


            ChatRoom room = null;
            try {
                room = SparkManager.getChatManager().getChatContainer().getChatRoom(bareJID);
            }
            catch (ChatRoomNotFoundException e) {
                // Ignore
            }
            
            //modified by sidney
            //群聊消息进来的时候激活群聊天房间
            if(isGroupChat){
            	
//				if (room == null) {
//					synchronized(MES_LOCK){
//					String chinaGroupRoomAddress = MUCUtil.getRoomAddress(message.getFrom());
//					GroupChatRoom chinaGrouproom = ChinaGroupUI.getChinaGroupMap().get(chinaGroupRoomAddress);
//					if (null != chinaGrouproom) {
//						ChatManager chatManager = SparkManager.getChatManager();
//						chatManager.getChatContainer().addChatRoom(chinaGrouproom);
//						chatManager.getChatContainer().activateChatRoom(chinaGrouproom);
//					} else {
//						// TODO在没有缓存chinaGroupRoom的时候，即用户没有加入chinaGroup群聊时怎么做
//					}
//					}
//            	}
            	return;
            }else{
            	// Check for non-existent rooms.
	            if (room == null) {
	                createOneToOneRoom(bareJID, message);
	            }
            }

            
        }
    }

    /**
     * Creates a new room if necessary and inserts an offline message.
     *
     * @param message The Offline message.
     */
    private void handleOfflineMessage(Message message) {
        if(!ModelUtil.hasLength(message.getBody())){
            return;
        }

        String bareJID = StringUtils.parseBareAddress(message.getFrom());
        ContactItem contact = contactList.getContactItemByJID(bareJID);
        String nickname = StringUtils.parseName(bareJID);
        if (contact != null) {
            nickname = contact.getDisplayName();
        }

        // Create the room if it does not exist.
        ChatRoom room = SparkManager.getChatManager().createChatRoom(bareJID, nickname, nickname);

        // Insert offline message
        room.getTranscriptWindow().insertMessage(nickname, message, ChatManager.FROM_COLOR, Color.white);
        room.addToTranscript(message, true);

        // Send display and notified message back.
        SparkManager.getMessageEventManager().sendDeliveredNotification(message.getFrom(), message.getPacketID());
        SparkManager.getMessageEventManager().sendDisplayedNotification(message.getFrom(), message.getPacketID());
    }

    /**
     * Creates a new room based on an anonymous user.
     *
     * @param bareJID the bareJID of the anonymous user.
     * @param message the message from the anonymous user.
     */
    private void createOneToOneRoom(String bareJID, Message message) {
        ContactItem contact = contactList.getContactItemByJID(bareJID);
        String nickname = StringUtils.parseName(bareJID);
        if (contact != null) {
            nickname = contact.getDisplayName();
        }
        else {
            // Attempt to load VCard from users who we are not subscribed to.
            VCard vCard = SparkManager.getVCardManager().getVCard(bareJID);
            if (vCard != null && vCard.getError() == null) {
                String firstName = vCard.getFirstName();
                String lastName = vCard.getLastName();
                String userNickname = vCard.getNickName();
                if (ModelUtil.hasLength(userNickname)) {
                    nickname = userNickname;
                }
                else if (ModelUtil.hasLength(firstName) && ModelUtil.hasLength(lastName)) {
                    nickname = firstName + " " + lastName;
                }
                else if (ModelUtil.hasLength(firstName)) {
                    nickname = firstName;
                }
            }
        }

        SparkManager.getChatManager().createChatRoom(bareJID, nickname, nickname);
        try {
            insertMessage(bareJID, message);
        }
        catch (ChatRoomNotFoundException e) {
            Log.error("Could not find chat room.", e);
        }
    }


    private void insertMessage(final String bareJID, final Message message) throws ChatRoomNotFoundException {
        ChatRoom chatRoom = SparkManager.getChatManager().getChatContainer().getChatRoom(bareJID);
        chatRoom.insertMessage(message);
        int chatLength = chatRoom.getTranscriptWindow().getDocument().getLength();
        chatRoom.getTranscriptWindow().setCaretPosition(chatLength);
        chatRoom.getChatInputEditor().requestFocusInWindow();
    }


    /**
     * Returns the Workspace TabbedPane. If you wish to add your
     * component, simply use addTab( name, icon, component ) call.
     *
     * @return the workspace JideTabbedPane
     */
    public SparkTabbedPane getWorkspacePane() {
        return workspacePane;
    }


    /**
     * Returns the <code>ContactList</code> associated with this workspace.
     *
     * @return the ContactList associated with this workspace.
     */
    public ContactList getContactList() {
        return contactList;
    }

    public void changeCardLayout(String layout) {
        cardLayout.show(cardPanel, layout);
    }

    public JPanel getCardPanel() {
        return cardPanel;
    }

    /**
     * Returns the <code>CommandPanel</code> of this Workspace.
     *
     * @return the CommandPanel.
     */
    public CommandPanel getCommandPanel() {
        return commandPanel;
    }

    public ChatTranscriptPlugin getTranscriptPlugin() {
        return transcriptPlugin;
    }
}
